home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / fimode.com / TP5PATCH.PAS
Encoding:
Pascal/Delphi Source File  |  1989-03-21  |  10.8 KB  |  363 lines

  1. {
  2.    TP5Patch   Version 1.0   3/20/89
  3.  
  4.    by Richard S. Sadowsky
  5.    CIS 74017,1670
  6.    donated to the public domain with no restrictions on use
  7. }
  8.  
  9. {$S-,R-,I-,V-}
  10.  
  11. program TP5Patch;
  12.  
  13. {
  14.   This program alters the File Mode byte associated with the opening of
  15.   Overlay files and text files opened for reading (with Reset). For some
  16.   reason, Turbo Pascal 5 ignores the global FileMode variable when opening
  17.   overlay and text files for reading. The program does not correct this
  18.   oversight, but does allow you to customize the Mode byte which is used to
  19.   open these files. If you look at the assembly snippets extracted from the
  20.   Overlay.TPU and System.TPU files, you'll note the problem (and see the
  21.   solution). The problem is that the file mode associated with these files is
  22.   hardcoded in the statement
  23.  
  24.     MOV  AX,3D00
  25.  
  26.   Luckily, this also means that solving the problem is as simple as replacing
  27.   the 00 in AL with a more appropriate value. By default, this program will
  28.   change the low byte to 40h (deny-none). This should result in these files
  29.   being shareable over a network without any bad side effects when running
  30.   single user. It is not appropriate here to enter a lengthy discussion of
  31.   other possible modes. Suffice to say that 40h should solve the main
  32.   problem--the inability to share Overlay and text files by more than one
  33.   program over a LAN.
  34.  
  35.   To explicitly set the file mode to a value other the default of 40h, use the
  36.   command line option -mxx, where xx is the hex value of the new mode. The mode
  37.   is always interpreted as a hex number, so a leading '$' or trailing 'h' is
  38.   not necessary (or permitted). The option -? shows a help message. Specifying
  39.   a file name on the command line will instruct TP5Patch to patch the
  40.   appropriate bytes in an already compiled TP5 EXE file. If a filename is
  41.   specified on the command line, then TP5Patch will not patch TURBO.TPL,
  42.   SYSTEM.TPU or OVERLAY.TPU (just the file mode or modes within the EXE file
  43.   are patched). The option -oxx is used to specify what the file mode currently
  44.   is so that a previously patched file may be re-patched to a new value.
  45.  
  46.   Example command lines:
  47.  
  48.   TP5Patch
  49.     Will look for SYSTEM.TPU, OVERLAY.TPU, and TURBO.TPL. It will patch those
  50.     found with the default file mode of 40h.
  51.  
  52.   TP5Patch -m42
  53.     Will look for SYSTEM.TPU, OVERLAY.TPU, and TURBO.TPL. It will patch those
  54.     found with the specified file mode of 42h.
  55.  
  56.   TP5Patch NETDEMO.EXE -m42
  57.     Will patch the TP5 program NETDEMO.EXE with the specified file mode of 42h.
  58.  
  59.   TP5Patch NETDEMO.EXE -m42 -o40
  60.     Will patch the TP5 program NETDEMO.EXE with the specified file mode of 42h.
  61.     NetDEMO had already been patched with 40h.
  62.  
  63.   This program DOES NOT MAKE A BACKUP of files before patching them. Please do
  64.   so before running this program.
  65.  
  66.  
  67.   Thee patching process boils down to searching for the following pieces of
  68.   code and patching the AL argument of the DOS function 3Dh call with the new
  69.   file mode.
  70.  
  71.   For overlay file mode look for the following code:
  72.           16           PUSH    SS
  73.           1F           POP     DS
  74.           B8003D       MOV     AX,3D00
  75.           CD21         INT     21
  76.           C3           RET
  77.  
  78.   For text file mode look for the following code:
  79.           B8003D       MOV     AX,3D00
  80.           817D02B1D7   CMP     WORD PTR [DI+02],D7B1
  81.           740D         JZ      1EF0
  82.           B002         MOV     AL,02
  83. }
  84. const
  85.   OverlayMatchPattern : Array[1..8] of Byte =
  86.                                          ($16,$1F,$B8,$00,$3D,$CD,$21,$C3);
  87.  
  88.   SystemMatchPattern : Array[1..14] of Byte =
  89.                                          ($B8,$00,$3D,$81,$7D,$02,$B1,$D7,
  90.                                           $74,$0D,$B0,$02,$FF,$05);
  91.   NewMode : Byte = $40;
  92.   JustReport : Boolean = FALSE;
  93.   PatchingExe : Boolean = FALSE;
  94.  
  95.   BufferSize = $FFF0;
  96.  
  97.   OverlayUnit = 'OVERLAY.TPU';
  98.   SystemUnit = 'SYSTEM.TPU';
  99.   TurboTPL = 'TURBO.TPL';
  100.  
  101. type
  102.   BufferType = Array[1..BufferSize] of Byte;
  103.  
  104. var
  105.   OvrFileSize : Word;
  106.   F : File;
  107.   Buffer : ^BufferType;
  108.   FileToPatch : String;
  109.  
  110. function Search(var BufToSearch; BufSize : Word;
  111.                 var BufToFind; FindSize : Word) : Word;
  112. {-A quick and dirty memory search routine}
  113. const
  114.   MaxWord = $FFFF;
  115.  
  116. type
  117.   SearchBuffer = Array[1..MaxWord] of Byte;
  118.  
  119. var
  120.   I,II : Word;
  121.   Buf : SearchBuffer absolute BufToSearch;
  122.   Find : SearchBuffer absolute BufToFind;
  123.   Found,StillMatches : Boolean;
  124. begin
  125.   Search := 0;
  126.   if (BufSize = 0) or (FindSize = 0) or (FindSize > BufSize) then
  127.     Exit;
  128.   I := 1;
  129.   Found := FALSE;
  130.   while (not Found) and (I <= (BufSize-FindSize+1)) do begin
  131.     if Buf[I] = Find[1] then begin
  132.       II := 1;
  133.       StillMatches := TRUE;
  134.       while (II < FindSize) and StillMatches do begin
  135.         if Buf[I+II] <> Find[Succ(II)] then
  136.           StillMatches := FALSE;
  137.         Inc(II);
  138.       end;
  139.       if (II = FindSize) and StillMatches then
  140.         Found := TRUE;
  141.     end;
  142.     if not Found then
  143.       Inc(I);
  144.   end;
  145.   if Found then
  146.     Search := I;
  147. end;
  148.  
  149. procedure ShowHelp;
  150. {-Display program options is halt}
  151. begin
  152.   WriteLn('TP5Patch [-?] [-m00] [filename.exe]'^M^J);
  153.   WriteLn('  -? displays this help message');
  154.   WriteLn('  -m sets file mode, may be specified in hex (default is 40h)');
  155.   WriteLn('  -o specifies the old mode to search for and patch');
  156.   WriteLn('  filename.exe refers to a Turbo Pascal EXE file to patch directly');
  157.   Halt;
  158. end;
  159.  
  160. function GetHexNum(S : String) : Word;
  161. {-Convert a string representing a hex number to a word (0 if invalid) }
  162. var
  163.   W,Code : Word;
  164.  
  165. begin
  166.   S := '$' + S;
  167.   Val(S,W,Code);
  168.   if Code = 0 then
  169.     GetHexNum := W
  170.   else
  171.     GetHexNum := 0;
  172. end;
  173.  
  174. function Nybble(x : Byte): Char;
  175. inline($58/            {  POP     AX     }
  176.        $24/$0F/        {  AND     AL,0F  }
  177.        $04/$90/        {  ADD     AL,90  }
  178.        $27/            {  DAA            }
  179.        $14/$40/        {  ADC     AL,40  }
  180.        $27/            {  DAA            }
  181.        $24/$7F);       {  AND     AL,7F  }
  182.  
  183. function HexByte(H : Byte): String;
  184. {-byte to hex string}
  185. begin
  186.   HexByte[0] := #2;
  187.   HexByte[1] := Nybble(H shr 4);
  188.   HexByte[2] := Nybble(H);
  189. end;
  190.  
  191. procedure GetOptions;
  192. {-Parse the command line}
  193. var
  194.   I : Byte;
  195.   OldMode : Byte;
  196.   Opt : String;
  197. begin
  198.   for I := 1 to ParamCount do begin
  199.     Opt := ParamStr(I);
  200.     if Opt[1] in ['-','/'] then
  201.       case UpCase(Opt[2]) of
  202.         'M' : begin
  203.                 NewMode := GetHexNum(Copy(Opt,3,Length(Opt)));
  204.                 WriteLn('Using a file mode of ',HexByte(NewMode),'h'^M^J);
  205.               end;
  206.         'O' : begin
  207.                 OldMode := GetHexNum(Copy(Opt,3,Length(Opt)));
  208.                 if OldMode <> 0 then begin
  209.                   OverlayMatchPattern[4] := OldMode;
  210.                   SystemMatchPattern[2] := OldMode;
  211.                 end;
  212.               end;
  213.         '?','H' : ShowHelp;
  214.         else begin
  215.           WriteLn('Invalid option ',Opt);
  216.         end;
  217.       end
  218.     else begin
  219.       FileToPatch := Opt;
  220.       PatchingEXE := TRUE;
  221.     end;
  222.   end;
  223. end;
  224.  
  225. procedure IOCheck(S : String);
  226. {-abort on I/O error  with message}
  227. var
  228.   E : Word;
  229. begin
  230.   E := IOResult;
  231.   if E <> 0 then begin
  232.     WriteLn('IOResult = ',E);
  233.     WriteLn(S);
  234.     Halt;
  235.   end;
  236. end;
  237.  
  238. procedure PatchMode(FName : String; var MatchPattern;
  239.                     MatchSize,OffsetInMatch : Word);
  240. {-Patch the specified file. Look for MatchPattern and patch at the
  241.  OffsetInMatch to NewMode.}
  242.  
  243. var
  244.   F : File;
  245.   Posit,FSize : Word;
  246.  
  247. begin
  248.   Assign(F,FName);
  249.   Reset(F,1);
  250.   if IOResult <> 0 then begin
  251.     WriteLn(FName,' not found');
  252.     Exit;
  253.   end;
  254.  
  255.   BlockRead(F,Buffer^,SizeOf(BufferType),FSize);
  256.   IOCheck('Error reading '+FName);
  257.   WriteLn(FSize,' bytes read from ',FName);
  258.   Posit := Search(Buffer^,FSize,MatchPattern,MatchSize);
  259.   if Posit = 0 then begin
  260.     WriteLn('Patch point not found');
  261.     Close(F);
  262.     if IOResult <> 0 then ;
  263.     Exit;
  264.   end;
  265.   Seek(F,Posit+(OffsetInMatch-2));
  266.   IOCheck('Seek error in '+FName);
  267.   BlockWrite(F,NewMode,SizeOf(Byte));
  268.   IOCheck('Error writing '+FName);
  269.   Close(F);
  270.   if IOResult = 0 then
  271.     WriteLn(FName,' patched')
  272.   else
  273.     WriteLn('I/O error saving ',FName);
  274. end;
  275.  
  276. procedure LookForPatchPoints(Size : Word; var TxtPosit,OvrPosit : Word);
  277. {-Find patch points in buffer}
  278. begin
  279.   OvrPosit := Search(Buffer^,Size,OverlayMatchPattern,
  280.                      SizeOf(OverlayMatchPattern));
  281.   TxtPosit := Search(Buffer^,Size,SystemMatchPattern,
  282.                      SizeOf(SystemMatchPattern));
  283. end;
  284.  
  285. procedure PatchByte(var F : File; Offs : Longint; var Bite : Byte);
  286. {-Seek Offs within F, and write new Byte Bite}
  287. begin
  288.   Seek(F,Offs);
  289.   IOCheck('Error seeking '+FileToPatch);
  290.   BlockWrite(F,Bite,SizeOf(Bite));
  291.   IOCheck('Error writing '+FileToPatch);
  292. end;
  293.  
  294. procedure PatchDiskFile;
  295. {-Patch a user specified EXE file}
  296. var
  297.   Overlap1,Overlap2 : ^BufferType;
  298.   Size,Posit : LongInt;
  299.   NumRead,TxtPosit,OvrPosit : Word;
  300.   FoundOvr,FoundText : Boolean;
  301.  
  302. begin
  303.   Assign(F,FileToPatch);
  304.   Reset(F,1);
  305.   IOCheck(FileToPatch + ' not found');
  306.   Size := FileSize(F);
  307.   FoundOvr := False;
  308.   FoundText := False;
  309.   Posit := 0;
  310.   while (Posit < Size) and (not (FoundOvr and FoundText)) do begin
  311.     Seek(F,Posit);
  312.     IOCheck('Error seeking '+FileToPatch);
  313.     BlockRead(F,Buffer^,SizeOf(Buffer^),NumRead);
  314.  
  315.     LookForPatchPoints(NumRead,TxtPosit,OvrPosit);
  316.     if TxtPosit > 0 then begin
  317.       FoundText := True;
  318.       PatchByte(F,Posit+TxtPosit,NewMode);
  319.     end;
  320.     if OvrPosit > 0 then begin
  321.       FoundOvr := True;
  322.       PatchByte(F,Posit+OvrPosit+2,NewMode);
  323.     end;
  324.  
  325.     Inc(Posit,NumRead);
  326.     if Posit < Size then
  327.       Dec(Posit,SizeOf(SystemMatchPattern));
  328.   end;
  329.   if FoundOvr then
  330.     WriteLn('Overlay file open patched in ',FileToPatch)
  331.   else
  332.     WriteLn(FileToPatch,' does not use TP5 Overlays (or already patched).');
  333.   if FoundText then
  334.     WriteLn('Text file open (reset) patched in ',FileToPatch)
  335.   else begin
  336.     WriteLn('Patch point not found for SYSTEM.TPU code in ',FileToPatch);
  337.     WriteLn('Either already patched or not a Turbo Pascal 5 program.');
  338.   end;
  339. end;
  340.  
  341. procedure PatchTPLandTPUs;
  342. {-Look for TURBO.TPL, SYSTEM.TPU and OVERLAY.TPU and patch those found}
  343. begin
  344.   PatchMode(OverlayUnit,OverlayMatchPattern,SizeOf(OverlayMatchPattern),4);
  345.   PatchMode(SystemUnit,SystemMatchPattern,SizeOf(SystemMatchPattern),2);
  346.   PatchMode(TurboTPL,OverlayMatchPattern,SizeOf(OverlayMatchPattern),4);
  347.   PatchMode(TurboTPL,SystemMatchPattern,SizeOf(SystemMatchPattern),2);
  348. end;
  349.  
  350. begin
  351.   WriteLn('TP5Patch version 1.0 - Patches Overlay and Text File Modes'^M^J);
  352.   FileToPatch := '';
  353.   GetOptions;
  354.   New(Buffer);
  355.  
  356.   if PatchingEXE then
  357.     PatchDiskFile
  358.   else
  359.     PatchTPLandTPUs;
  360.  
  361.   Dispose(Buffer);
  362. end.
  363.